package NG::Sys::Loop;
use strict;
use warnings;
use utf8;

my @event_names = qw(
before_loop
prepare_loop
enter_loop
init_process
before_process
process
after_process
done_process
leave_loop
after_loop
delay_loop
);

sub process_error {
	my ($app,$scope,$error) = @_;
	print "LOOP_ERROR[$scope]: ",$error,"\n";
}
sub run {
	my ($app) = @_;
	_init($app);

	unless(_before_loop($app)){
		return;
	}
	unless(_prepare_loop($app)){
		return;
	}

	for(;;){
		unless(_enter_loop($app)){
			last;
		}
		
		my @args = _init_process($app);
		if(_before_process($app,@args)){
			unless(_process($app,@args)){
				_after_process($app,@args);
			}
		}
		_done_process($app,@args);
		
		if(_leave_loop($app)){
			last;
		}
		_delay_loop($app);
	}

	_after_loop($app);
	_done($app);
}
################################################################
# private method, not overraded
sub _init {
	my ($app) = @_;
	$app->{events} = {};
	for my $ev (@event_names){
		my $sub = "on_".$ev;
		$app->{events}{$ev} = main->can($sub) || $app->can($sub);
	}
}
sub _done {
	my ($app) = @_;
}
sub _before_loop{
	my ($app) = @_;
	my $ret = 1;
	if(my $sub = $app->{events}{before_loop}){
		eval {
			$ret = $sub->($app);
		};
		if($@){
			$app->process_error("BEFORE_LOOP",$@);
			undef $ret;
		}
	}
	return $ret;
}
sub _after_loop{
	my ($app) = @_;
	if(my $sub = $app->{events}{after_loop}){
		eval {
			$sub->($app);
		};
		if($@){
			$app->process_error("AFTER_LOOP",$@);
		}
	}
}
sub _prepare_loop{
	my ($app) = @_;
	if(my $sub = $app->{events}{prepare_loop}){
		eval {
			$sub->($app);
		};
		if($@){
			$app->process_error("PREPARE_LOOP",$@);
			return 0;
		}
	}
	return 1;
}
sub _enter_loop {
	my ($app) = @_;
	my $ret = 1;
	if(my $sub = $app->{events}{enter_loop}){
		eval {
			$ret = $sub->($app);
		};
		if($@){
			$app->process_error("ENTER_LOOP",$@);
			undef $ret;
		}
	}
	return $ret;
}
sub _leave_loop {
	my ($app) = @_;
	my $ret = 1;
	if(my $sub = $app->{events}{leave_loop}){
		eval {
			$ret = $sub->($app);
		};
		if($@){
			$app->process_error("LEAVE_LOOP",$@);
			$ret = 1;
		}
	}
	return $ret;
}
sub _init_process {
	my ($app) = @_;
	my @ret;
	if(my $sub = $app->{events}{init_process}){
		eval {
			@ret = $sub->($app);
		};
		if($@){
			$app->process_error("INIT_PROCESS",$@);
		}
	}
	return @ret;
}
sub _done_process {
	my ($app) = @_;
	if(my $sub = $app->{events}{done_process}){
		eval {
			$sub->($app);
		};
		if($@){
			$app->process_error("DONE_PROCESS",$@,@_);
		}
	}
}
sub _before_process {
	my ($app) = @_;
	my $ret = 1;
	if(my $sub = $app->{events}{before_process}){
		eval {
			$ret = $sub->(@_);
		};
		if($@){
			$app->process_error("BEFORE_PROCESS",$@,@_);
			undef $ret;
		}
	}
	return $ret;
}
sub _after_process {
	my ($app) = @_;
	if(my $sub = $app->{events}{after_process}){
		eval {
			$sub->(@_);
		};
		if($@){
			$app->process_error("AFTER_PROCESS",$@,@_);
		}
	}
}
sub _process {
	my ($app) = @_;
	my $ret = 0;
	if(my $sub = $app->{events}{process}){
		eval {
			$ret = $sub->(@_);
		};
		if($@){
			$app->process_error("PROCESS",$@,@_);
			$ret = 1;
		}
	}
	return $ret;
}
sub _delay_loop {
	my ($app) = @_;
	if(my $sub = $app->{events}{delay_loop}){
		eval {
			$sub->(@_);
		};
		if($@){
			$app->process_error("DELAY_LOOP",$@,@_);
		}
	}
}

#sub on_before_loop {
#	my ($app) = @_;
#}
#sub on_prepare_loop {
#	my ($app) = @_;
#}
#sub on_enter_loop {
#	my ($app) = @_;
#}
#sub on_init_process {
#	my ($app) = @_;
#}
#sub on_before_process {
#	my ($app) = @_;
#}
#sub on_process {
#	my ($app) = @_;
#}
#sub on_after_process {
#	my ($app) = @_;
#}
#sub on_done_process {
#	my ($app) = @_;
#}
#sub on_delay_loop {
#	my ($app) = @_;
#}
#sub on_leave_loop {
#	my ($app) = @_;
#}
#sub on_after_loop {
#	my ($app) = @_;
#}
#
1;
